#include "image_scale.h"
#include "math.h"

typedef union _argb32
{
    uint32_t value;
    uint8_t byte[4];
    struct
    {
        uint8_t b;
        uint8_t g;
        uint8_t r;
        uint8_t a;
    };
} argb32_t;

static void bicubic_find_near_4x4_matrix(uint16_t *pos_array, float pos, uint16_t max);
static float f_bicubic(float x);

static argb32_t operator+(argb32_t &&pixel1, argb32_t &&pixel2);
static argb32_t operator*(argb32_t &pixel, float scale);

/**
 * @brief 图片缩放(邻近取值法)
 */
void *image_near_scale_argb32(const void *source, uint16_t in_width, uint16_t in_height, float w_scale, float h_scale)
{
    uint16_t out_width = in_width * w_scale;
    uint16_t out_height = in_height * h_scale;

    auto target = new uint32_t[(uint32_t)out_width * out_height];

    for (int i = 0; i < out_height; i++)
    {
        float pixel_y = i / h_scale;
        uint16_t source_y = (uint16_t)(pixel_y + 0.5f); // 取纵坐标邻近值

        uint32_t source_line_start_index = (uint32_t)source_y * in_width;
        uint32_t target_line_start_index = (uint32_t)i * out_width;

        for (int j = 0; j < out_width; j++)
        {
            float pixel_x = j / w_scale;
            uint16_t source_x = (uint16_t)(pixel_x + 0.5f); // 取横坐标邻近值

            target[target_line_start_index + j] = ((const uint32_t *)source)[source_line_start_index + source_x]; // 根据邻近坐标取颜色
        }
    }

    return target;
}

/**
 * @brief 图片缩放(双线性插值法)
 */
void *image_bilinear_scale_argb32(const void *source, uint16_t in_width, uint16_t in_height, float w_scale, float h_scale)
{
    uint16_t out_width = in_width * w_scale;
    uint16_t out_height = in_height * h_scale;
    argb32_t near_matrix[2][2]; // {{[0,0], [1,0]}, {[0,1], [1,1]}} {x, y}

    auto target = new uint32_t[(uint32_t)out_width * out_height];

    for (int i = 0; i < out_height; i++)
    {
        float pixel_y = i / h_scale;
        uint16_t source_y0 = (uint16_t)pixel_y;
        uint16_t source_y1 = source_y0 + 1;

        float scale_y0 = 1 - (pixel_y - source_y0);
        float scale_y1 = 1 - (source_y1 - pixel_y);

        uint32_t source_line_start_index_0 = (uint32_t)source_y0 * in_width;
        uint32_t source_line_start_index_1 = (uint32_t)source_y1 * in_width;
        uint32_t target_line_start_index = (uint32_t)i * out_width;

        for (int j = 0; j < out_width; j++)
        {
            float pixel_x = j / w_scale;
            uint16_t source_x0 = (uint16_t)pixel_x;
            uint16_t source_x1 = source_x0 + 1;

            float scale_x0 = 1 - (pixel_x - source_x0);
            float scale_x1 = 1 - (source_x1 - pixel_x);

            near_matrix[0][0].value = ((const uint32_t *)source)[source_line_start_index_0 + source_x0]; // 取周围4个点的颜色数据
            near_matrix[0][1].value = ((const uint32_t *)source)[source_line_start_index_0 + source_x1];
            near_matrix[1][0].value = ((const uint32_t *)source)[source_line_start_index_1 + source_x0];
            near_matrix[1][1].value = ((const uint32_t *)source)[source_line_start_index_1 + source_x1];

            argb32_t pixel_0 = near_matrix[0][0] * scale_x0 + near_matrix[0][1] * scale_x1; // 计算[0,0], [1,0] 之间的线性插值 m
            argb32_t pixel_1 = near_matrix[1][0] * scale_x0 + near_matrix[1][1] * scale_x1; // 计算[0,1], [1,1] 之间的线性插值 n
            argb32_t pixel = pixel_0 * scale_y0 + pixel_1 * scale_y1;                       // 计算m, n 之间的线性插值 p

            target[target_line_start_index + j] = pixel.value;
        }
    }

    return target;
}

/**
 * @brief 图片缩放(双三次插值法)
 */
void *image_bicubic_scale_argb32(const void *source, uint16_t in_width, uint16_t in_height, float w_scale, float h_scale)
{
    uint16_t out_width = in_width * w_scale;
    uint16_t out_height = in_height * h_scale;

    /**
     * 像素坐标[x,y]
     * [0,0], [1,0], [2,0], [3,0],
     * [0,1], [1,1], [2,1], [3,1],
     * [0,2], [1,2], [2,2], [3,2],
     * [0,3], [1,3], [2,3], [3,3],
     *
     * 数组下标
     * [0][0] [0][1] [0][2] [0][3]
     * [1][0] [1][1] [1][2] [1][3]
     * [2][0] [2][1] [2][2] [2][3]
     * [3][0] [3][1] [3][2] [3][3]
     */
    argb32_t near_matrix[4][4];
    float weight_matrix_hor[4]; // 每个像素的水平权重(相同y，不同x)
    float weight_matrix_ver[4]; // 每个像素的垂直权重(相同x，不同y)

    auto target = new uint32_t[(uint32_t)out_width * out_height];

    for (int i = 0; i < out_height; i++)
    {
        uint16_t source_y[4];

        float pixel_y = i / h_scale;
        bicubic_find_near_4x4_matrix(source_y, pixel_y, in_height - 1); // 找到邻近的4x4矩阵y坐标

        // 计算每个像素垂直权重
        for (int y = 0; y < 4; y++)
            weight_matrix_ver[y] = f_bicubic(pixel_y - source_y[y]);

        for (int j = 0; j < out_width; j++)
        {
            uint16_t source_x[4];

            float pixel_x = j / w_scale;
            bicubic_find_near_4x4_matrix(source_x, pixel_x, in_width - 1); // 找到邻近的4x4矩阵x坐标

            // 计算每个像素的水平权重
            for (int x = 0; x < 4; x++)
                weight_matrix_hor[x] = f_bicubic(pixel_x - source_x[x]);

            float byte_f[4] = {0};

            // 取矩阵每个像素颜色值并计算最终权重和颜色
            for (int y = 0; y < 4; y++)
            {
                for (int x = 0; x < 4; x++)
                {
                    near_matrix[y][x].value = ((const uint32_t *)source)[(uint32_t)source_y[y] * in_width + source_x[x]]; // 取颜色
                    float weight = weight_matrix_hor[x] * weight_matrix_ver[y];                                           // 计算最终权重

                    for (int k = 0; k < 4; k++)
                        byte_f[k] += near_matrix[y][x].byte[k] * weight;
                }
            }

            argb32_t pixel;
            for (int k = 0; k < 4; k++) // 限制数值范围，防止溢出产生噪点
            {
                if (byte_f[k] > 255)
                    byte_f[k] = 255;
                else if (byte_f[k] < 0)
                    byte_f[k] = 0;

                pixel.byte[k] = byte_f[k];
            }

            target[i * out_width + j] = pixel.value;
        }
    }

    return target;
}

/**
 * @brief 找到目标像素点周围4x4的x或y坐标矩阵
 */
static void bicubic_find_near_4x4_matrix(uint16_t *pos_array, float pos, uint16_t max)
{
    if (pos - 1 < 0)
        pos_array[0] = 0;
    else if (pos + 1 >= max)
        pos_array[0] = max - 3;
    else
        pos_array[0] = (uint16_t)pos - 1;

    for (int i = 1; i < 4; i++)
        pos_array[i] = pos_array[0] + i;
}

/**
 * @brief Bicubic函数
 */
static float f_bicubic(float x)
{
    static constexpr float a = -0.5f;

    float out = 0;
    float abs_x = fabsf(x);
    float pow_x = abs_x * abs_x;
    float cube_x = pow_x * abs_x;

    if (abs_x <= 1)
        out = (2 + a) * cube_x - (3 + a) * pow_x + 1;
    else if (abs_x <= 2)
        out = a * cube_x - 5 * a * pow_x + 8 * a * abs_x - 4 * a;

    return out;
}

/**
 * @brief 运算符重载+
 */
static argb32_t operator+(argb32_t &&pixel1, argb32_t &&pixel2)
{
    argb32_t ret;

    ret.a = pixel1.a + pixel2.a;
    ret.r = pixel1.r + pixel2.r;
    ret.g = pixel1.g + pixel2.g;
    ret.b = pixel1.b + pixel2.b;

    return ret;
}

/**
 * @brief 运算符重载*
 */
static argb32_t operator*(argb32_t &pixel, float scale)
{
    argb32_t ret;

    ret.a = pixel.a * scale;
    ret.r = pixel.r * scale;
    ret.g = pixel.g * scale;
    ret.b = pixel.b * scale;

    return ret;
}
